﻿#include "f5g006_api.h"
#include "f5g006_ds.h"
#include "logutils.h"
#include <map>

#define begin_func(...) \
    do { if (globalLogger().isLoggable(LL_INFO)) \
        globalLogger().writeln(LL_INFO, "Begin %s (%s)", __FUNCTION__, stringFormat(__VA_ARGS__)->c_str()); \
    } while(0);

#define return_success() \
    do { if (globalLogger().isLoggable(LL_INFO)) globalLogger().writeln(LL_INFO, "Exit %s (ret = 0)\n", __FUNCTION__); \
        return 0; \
    } while(0);

#define return_success_with_args(...) \
    do { if (globalLogger().isLoggable(LL_INFO)) \
            globalLogger().writeln(LL_INFO, "Exit %s (ret = 0, %s)\n", __FUNCTION__, stringFormat(__VA_ARGS__)->c_str()); \
        return 0; \
    } while(0);

#define return_failure(ret) \
    do { if (globalLogger().isLoggable(LL_ERROR)) \
            globalLogger().writeln(LL_ERROR, "%s 失败: %d, %s\n", __FUNCTION__, ret, F5G006_StrError(ret)); \
        return ret; \
    } while(0);


static const long SHORT_TIMEOUT_VALUE = 5000;
static const long LONG_TIMEOUT_VALUE = 30000;

static std::mutex g_mutex;
static std::map<F5G006_HANDLE, F5G006_DeviceService*> g_objs;



bool F5G006_API F5G006_EnableFileLog(const char* logsDir, const char* fileNamePrefix)
{
    return globalLogger().enableFileLog(logsDir, fileNamePrefix);
}


void F5G006_API F5G006_DisableFileLog()
{
    globalLogger().disableFileLog();
}


bool F5G006_API F5G006_EnableConsoleLog()
{
    return globalLogger().enableConsoleLog();
}


void F5G006_API F5G006_DisableConsoleLog()
{
    globalLogger().disableConsoleLog();
}


void F5G006_API F5G006_SetLogLevel(int level)
{
    globalLogger().setCurrentLevel(level);
}


int F5G006_API F5G006_Connect(const char* portName, F5G006_HANDLE* phdev)
{
    int ret;
    std::lock_guard<std::mutex> locker(g_mutex);

    begin_func("portName = %s, phdev @ 0x%p", portName, phdev);

    std::unique_ptr<Transport> transport(new SerialTransport(
        portName,
        38400,
        SerialTransport::Parity::Even));
    if (transport == nullptr) {
        return_failure(F5G006_E_NO_MEMORY);
    }

    ret = tresultToApiError(transport->open());
    if (ret != 0) {
        return_failure(ret);
    }

    F5G006_DeviceService* pds = new F5G006_DeviceService(std::move(transport));
    if (pds == nullptr) {
        return_failure(F5G006_E_NO_MEMORY);
    }

    *phdev = (F5G006_HANDLE)pds;
    g_objs[*phdev] = pds;

    return_success_with_args("*phdev = 0x%p", *phdev);
}


int F5G006_API F5G006_Disconnect(F5G006_HANDLE hdev)
{
    std::lock_guard<std::mutex> locker(g_mutex);

    begin_func("hdev @ 0x%p", hdev);

    F5G006_DeviceService* pds = g_objs[hdev];
    if (pds == nullptr) {
        return_failure(F5G006_E_INVALID_HANDLE);
    }

    g_objs.erase(hdev);
    delete pds;

    return_success();
}


int F5G006_API F5G006_GetFWVersion(F5G006_HANDLE hdev, char* pOutBuf, int* pOutLen)
{
    int ret;
    std::lock_guard<std::mutex> locker(g_mutex);

    begin_func("hdev @ 0x%p, pOutBuf @ 0x%p, pOutLen @ 0x%p",
        hdev, pOutBuf, pOutLen);

    F5G006_DeviceService* pds = g_objs[hdev];
    if (pds == nullptr) {
        return_failure(F5G006_E_INVALID_HANDLE);
    }

    if (pOutBuf == nullptr || pOutLen == nullptr) {
        return_failure(F5G006_E_INVALID_PARAM);
    }

    pOutBuf[0] = 0;
    *pOutLen = 0;

    const char CMP = '1';
    ret = pds->doCommand('v', '0', &CMP, 1, SHORT_TIMEOUT_VALUE);
    if (ret != 0) {
        return_failure(ret);
    }

    if (pds->m_lastRDTSize > 0) {
        memcpy(pOutBuf, pds->m_pLastRDT, pds->m_lastRDTSize);
        pOutBuf[pds->m_lastRDTSize] = 0;
        *pOutLen = pds->m_lastRDTSize;
    }

    return_success_with_args("pOutBuf = %s, *pOutLen = %d", pOutBuf, *pOutLen);
}


int F5G006_API F5G006_GetSensorInfo(F5G006_HANDLE hdev, uint8_t* pOutBuf, int* pOutLen)
{
    int ret;
    std::lock_guard<std::mutex> locker(g_mutex);

    begin_func("hdev @ 0x%p, pOutBuf @ 0x%p, pOutLen @ 0x%p",
        hdev, pOutBuf, pOutLen);

    F5G006_DeviceService* pds = g_objs[hdev];
    if (pds == nullptr) {
        return_failure(F5G006_E_INVALID_HANDLE);
    }

    if (pOutBuf == nullptr || pOutLen == nullptr) {
        return_failure(F5G006_E_INVALID_PARAM);
    }

    pOutBuf[0] = 0;
    *pOutLen = 0;

    ret = pds->doCommand('v', '1', SHORT_TIMEOUT_VALUE);
    if (ret != 0) {
        return_failure(ret);
    }

    if (pds->m_lastRDTSize > 0) {
        memcpy(pOutBuf, pds->m_pLastRDT, pds->m_lastRDTSize);
        pOutBuf[pds->m_lastRDTSize] = 0;
        *pOutLen = pds->m_lastRDTSize;
    }

    return_success_with_args("pOutBuf = %s, *pOutLen = %d",
        bytesToHexString(pOutBuf, *pOutLen)->c_str(), *pOutLen);
}


int F5G006_API F5G006_GetStatus(F5G006_HANDLE hdev, uint8_t* pSuppBinStat, uint8_t* pRetainBinStat)
{
    int ret;
    std::lock_guard<std::mutex> locker(g_mutex);

    begin_func("hdev @ 0x%p, pSuppBinStat @ 0x%p, pRetainBinStat @ 0x%p",
        hdev, pSuppBinStat, pRetainBinStat);

    F5G006_DeviceService* pds = g_objs[hdev];
    if (pds == nullptr) {
        return_failure(F5G006_E_INVALID_HANDLE);
    }

    if (pSuppBinStat == nullptr || pRetainBinStat == nullptr) {
        return_failure(F5G006_E_INVALID_PARAM);
    }

    ret = pds->doCommand('v', '1', SHORT_TIMEOUT_VALUE);
    if (ret != 0) {
        return_failure(ret);
    }

    if (pds->m_lastRDTSize < 3) {
        log_error("获取的传感器数据小于3字节");
        return_failure(F5G006_E_RECEPTION);
    }

    uint8_t b;
    b = pds->m_pLastRDT[2] & 0x31;
    if (!(b & 1)) { // DCT = 0
        *pSuppBinStat = F5G006_SUPPBIN_NOT_PRESENT;
    }
    else {    // DCT = 1
        b &= ~1;
        if (b == 0) { // EN 和 NE 同时为0
            *pSuppBinStat = F5G006_SUPPBIN_NOT_PRESENT;
        }
        else if (!(b & 0x10)) { // !EN = 1, EN=0
            *pSuppBinStat = F5G006_SUPPBIN_EMPTY;
        }
        else if (b & 0x20) { // NE = 1
            *pSuppBinStat = F5G006_SUPPBIN_LOW;
        }
        else { // NE=0, EN=1
            *pSuppBinStat = F5G006_SUPPBIN_HIGH;
        }
    }

    b = pds->m_pLastRDT[0] & 0x7;
    if (!(b & 2)) { // STK = 0
        *pRetainBinStat = F5G006_RETAINBIN_NOT_PRESENT;
    }
    else { // STK = 1
        b &= ~2;
        if (b == 5) {    // CST 和 FULL 同时为1
            *pRetainBinStat = F5G006_RETAINBIN_FULL;
        }
        else if (b == 0) { // CST 和 FULL 同时为0
            *pRetainBinStat = F5G006_RETAINBIN_NO_CARD_IN;
        }
        else if (b & 1) { // CST = 1
            *pRetainBinStat = F5G006_RETAINBIN_NOT_FULL;
        }
        else {
            *pRetainBinStat = F5G006_RETAINBIN_NOT_PRESENT;
        }
    }

    return_success_with_args("*pSuppBinStat = %d, *pRetainBinStat = %d",
        *pSuppBinStat, *pRetainBinStat);
}


int F5G006_API F5G006_Dispense(F5G006_HANDLE hdev)
{
    int ret;
    std::lock_guard<std::mutex> locker(g_mutex);

    begin_func("hdev = 0x%p", hdev);

    F5G006_DeviceService* pds = g_objs[hdev];
    if (pds == nullptr) {
        return_failure(F5G006_E_INVALID_HANDLE);
    }
    // uint8_t cmp = '1';
    // ret = pds->doCommand('v', 'C', &cmp, 1, LONG_TIMEOUT_VALUE);
    ret = pds->doCommand('v', 'C', LONG_TIMEOUT_VALUE);
    if (ret != 0) {
        return_failure(ret);
    }

    return_success();
}


F5G006_API const char* F5G006_StrError(int ret)
{
    switch (ret) {
    case F5G006_NO_ERROR: return "操作成功";
    case F5G006_E_DEV_NOT_FOUND: return "设备未找到";
    case F5G006_E_CONN_LOST: return "连接丢失";
    case F5G006_E_ACCESS: return "设备拒绝访问";
    case F5G006_E_INVALID_HANDLE: return "无效句柄";
    case F5G006_E_INVALID_PARAM: return "参数不正确";
    case F5G006_E_NO_MEMORY: return "无可用内存";
    case F5G006_E_IO_ERROR: return "I/O 错误";
    case F5G006_E_TIMEOUT: return "通信超时";
    case F5G006_E_NO_ACK: return "未收到传输确认字符ACK";
    case F5G006_E_RECEPTION: return "接收的数据不正确";
    case F5G006_E_INTERNAL_ERROR: return "内部错误 (软件错误, 或者固件错误)";
    case F5G006_E_UNKNOWN_ERROR: return "未知错误";
    case F5G006_E_POWER_DOWN: return "设备掉电";
    case F5G006_E_SUPPLY_BIN_EMPTY: return "发卡箱空";
    case F5G006_E_NO_SUPPLY_BIN: return "无发卡箱";
    case F5G006_E_HARDWARE_ERROR: return "硬件错误";
    case F5G006_E_NO_RETAIN_BIN: return "无回收箱";
    case F5G006_E_RETAIN_BIN_FULL: return "回收箱满";
    case F5G006_E_FAILURE: return "命令失败";
    default: return "其它错误";
    }
}
